﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using ExamUtils;
using ExamCommon;
using System.Collections.Concurrent;
using System.Net.NetworkInformation;

namespace ExamServerBLL
{
    public static class ServerSocketService
    {
        /// <summary>
        /// 声明服务端socket套接字
        /// </summary>
        private static Socket serverTCPSocket = null;

        /// <summary>
        /// 创建用来专门作为监听来电等待工作的线程
        /// </summary>
        private static Thread listenThread = null;

        /// <summary>
        /// 已连接的socket集合, 保存远程连接的socket
        /// </summary>
        public static ConcurrentDictionary<string, Socket> conncetedSocketDictionary = new ConcurrentDictionary<string, Socket>();

        /// <summary>
        /// 开启serverSocket服务
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool CreateServerTCPSocket(int port)
        {
            try
            {
                // 新建serverSocket
                serverTCPSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // 创建IP地址对象
                IPAddress serverIPAddress = IPAddress.Any;
                // 创建端口对象
                IPEndPoint iPEndPoint = new IPEndPoint(serverIPAddress, port);
                // serverSocket绑定监听绑定端口号和IP
                serverTCPSocket.Bind(iPEndPoint);
                // 开启服务监听，参数为最大挂起队列的连接数（并发），已连接的不计, 每调用Accept函数一下即从这个等待队列中拿出一个连接
                serverTCPSocket.Listen(100);
            }
            catch (Exception ex)
            {
                Console.WriteLine("error: 初始化socket失败! 原因: " + ex.Message);
                return false;
            }

            // 初始化监听线程
            listenThread = new Thread(ListenSocket);
            // 设置线程后台运行
            listenThread.IsBackground = true;
            // 将serverTCPSocket传给子线程执行,启动线程
            listenThread.Start(serverTCPSocket);
            Console.WriteLine("socket服务启动成功!");
            return true;
        }

        /// <summary>
        /// 监听相应的端口, 等待客户端连接
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        private static void ListenSocket(Object socket)
        {
            if (socket == null)
            {
                return;
            }
            // object转换为Socket
            Socket serverSocket = socket as Socket;
            // 该循环只是负责接受客户端连接, 不提供连接后的服务
            while (true)
            {
                try
                {
                    // 调用Accept()取出当前连接的socket
                    Socket acceptSocket = serverSocket.Accept();
                    // 将已经建立的连接添加至conncetedSocketDictionary集合中
                    conncetedSocketDictionary.TryAdd(GenerateKey(acceptSocket), acceptSocket);
                    // 每当建立一个连接开启一个线程用于提供服务, 使用线程池
                    ThreadPool.QueueUserWorkItem(ReceiveClientMsg, acceptSocket);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("error: 接受客户端连接出现异常: " + ex.Message);
                    //listenThread.Abort(ex.Message);
                }
            }
        }

        /// <summary>
        /// 不停的接受来自客户端的消息
        /// </summary>
        /// <param name="socket"></param>
        private static void ReceiveClientMsg(Object socket)
        {
            if (socket == null)
            {
                return;
            }
            // object转换为Socket
            Socket connectedSocket = socket as Socket;
            Console.WriteLine(connectedSocket.RemoteEndPoint.ToString() + " :连接成功!");


            while (true)
            {
                // 接收消息, 只负责收消息
                Message message = SocketHelper.ReceiveMessage(connectedSocket);
                // 如果消息为null,则说明客户端被关闭
                if (message == null)
                {
                    break;
                }
                // 将消息交给消息处理函数
                MessageHandler handling = new MessageHandler();
                handling.HandleMessage(message);
            }
        }

        /// <summary>
        /// 向客户端发送消息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public static bool SendMessage(Socket socket, Message message)
        {
            return SocketHelper.SendMessage(socket, message);
        }

        /// <summary>
        /// 关闭服务器端的ServerSocket连接
        /// </summary>
        public static void CloseServerTCPSocket()
        {
            if (serverTCPSocket != null)
            {
                // TODO 发送消息通知客户端服务已经关闭

                if (serverTCPSocket.Connected)
                {
                    serverTCPSocket.Shutdown(SocketShutdown.Both);
                }
                // 关闭socket
                serverTCPSocket.Close();
                serverTCPSocket.Dispose();
                serverTCPSocket = null;
                // 通知监听线程中断
                listenThread.Abort();
                listenThread = null;
            }
        }

        /// <summary>
        /// 用于生成conncetedSocketDictionary集合中的key
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public static string GenerateKey(Socket socket)
        {
            if (socket == null)
            {
                return string.Empty;
            }
            return socket.LocalEndPoint.ToString() + "-" + socket.RemoteEndPoint.ToString();
        }

        /// <summary>
        /// 用于生成conncetedSocketDictionary集合中的key, 重载函数
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static string GenerateKey(Message msg)
        {
            if (msg == null)
            {
                return string.Empty;
            }
            return msg.toEndPoint.ToString() + "-" + msg.fromEndPoint.ToString();
        }

        /// <summary>
        /// 根据端口号获取当前端口号是否处于监听状态
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static bool GetSocketStatus(int port)
        {
            bool isUsed = false;
            IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
            foreach (var item in ipEndPoints)
            {
                if (item.Port == port)
                {
                    isUsed = true;
                    break;
                }
            }
            return isUsed;
        }
    }
}
